/*
 * Socket.cpp
 *
 *  Created on: 16/06/2014
 *      Author: nicolas
 */

#include "Socket.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

Socket::Socket() {
	sockfd = 0;
}

Socket::Socket(int sockfd) {
	this->sockfd = sockfd;
}

int Socket::bindTo(int port) {
	char mostrar[120]; /* mensajes en la pantalla */

	struct sockaddr_in serv_addr;
	/*			INICIALIZACION DEL SERVER

	 * 		Inicializar la estructura del socket:
	 * bzero(): borrar la estructura
	 * htonl(): Host-TO-Network Long integer (Byte order)
	 * htons(): Host-TO-Network Short integer (Byte order)
	 */

	bzero((char *) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET; /* familia protocolos TCP/IP */
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY ); /* cualquier cliente */
	serv_addr.sin_port = htons((u_short) port); /* port en formato red */
	/*
	 * 		Crear un socket para TCP (un Internet stream socket).
	 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		return (-1); /* error no se puede abrir el stream socket */
	}
	sprintf(mostrar, "server: socket creado %d\n", sockfd);
	write(fileno(stdout), mostrar, strlen(mostrar));
	/*
	 * 		Vincular el socket con la direccion local
	 */
	if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
		return (-1); /* error: no se puede hacer el bind */
	}
	sprintf(mostrar, "server: se hizo el bind\n");
	write(fileno(stdout), mostrar, strlen(mostrar));

	return 0;
}

int Socket::listenTo(int size) {
	char mostrar[120]; /* mensajes en la pantalla */
	listen(sockfd, size);
	sprintf(mostrar, "server: se hizo el listen con el socket %d\n", sockfd);
	write(fileno(stdout), mostrar, strlen(mostrar));
	return 0;
}

int Socket::connectTo(const char *server, int puerto) {
	struct sockaddr_in serv_addr;
	struct hostent *ptr_server; /*puntero a dir del server(gethostbyname)*/
	/*
	 *			ALOCAR EL SOCKET Y COMUNICARSE CON EL SERVER
	 *
	 * 	Completar la estructura del socket "serv_addr" con la direccion del
	 * 	server y port al que nos queremos connectar.
	 *
	 * 	Paso 1: Borrar la estructura (poner en cero)
	 */
	bzero((char *) &serv_addr, sizeof(serv_addr));

	/*	Paso 2:  Inicializar familia de direcciones (protocolo IP)
	 */
	serv_addr.sin_family = AF_INET;

	/*
	 *	Paso 3: Cargar port en el socket:
	 *	        Convertir Host-TO-Network-Short integer
	 */
	serv_addr.sin_port = htons(puerto); /* valido */
	/*
	 *	Paso 4: Cargar direccion del server en el socket
	 *	        Convertir nombre del host en su direccion
	 */
	ptr_server = gethostbyname(server);
	if (((char *) ptr_server) == NULL) { /* no existe: aviso y terminar */
		return (-2);
	} else
		memcpy(&serv_addr.sin_addr, ptr_server->h_addr, ptr_server->h_length);
		/*
		 * 	Abrir como un socket de TCP (Internet stream socket).
		 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		return (-1); /* error en la creacion del socket */
	}
	/*
	 * 	Connectarse al server.
	 */
	if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
			< 0) {
		return (-1); /* error en el connect */
	}
	/*
	 *		se conecto exitosamente
	 */
	return 0;
}

int Socket::connectTo(struct sockaddr *server, int puerto) {
	struct sockaddr_in* sserv = (struct sockaddr_in*) server;
	struct sockaddr_in serv_addr;

	bzero((char *) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(puerto); /* valido */
	memcpy(&serv_addr.sin_addr, &sserv->sin_addr, sizeof(sserv->sin_addr));

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		return (-1); /* error en la creacion del socket */
	}
	/*
	 * 	Connectarse al server.
	 */
	if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
			< 0) {
		return (-1); /* error en el connect */
	}
	/*
	 *		se conecto exitosamente
	 */
	return 0;
}

int Socket::acceptFrom() {
	struct sockaddr_in cli_addr;
	unsigned clilen = sizeof(cli_addr);
	int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
	if (newsockfd == -1) {
		perror("server: error en el accept");
		return -1;
	}
	return newsockfd;
}

int Socket::send(void *datos, size_t nbytes) {

	int enviados; /* Cantidad de bytes enviados. */
	int ult_envio; /* Cantidad de bytes enviados en el
	 ultimo envio. */
	/* Envia los datos. */
	enviados = 0;

	while (nbytes) {
		ult_envio = write(sockfd, ((char *) datos) + enviados, nbytes);
		if (ult_envio == -1) {
			perror("Error en el send");
			return enviados; /* error */
		}
		enviados += ult_envio;
		nbytes -= ult_envio;
	}

	/* Devuelve la cantidad de bytes enviados. */

	return enviados;
}

int Socket::recv(void *datos, size_t nbytes) {
	/* Variables. */

	int ult_lec; /* Cantidad de bytes leidos en la ultima lectura. */
	int leidos; /* Cantidad de bytes leidos. */
	/* Lee  */
	leidos = 0;

	while (nbytes) {
		ult_lec = read(sockfd, ((unsigned char *) datos) + leidos, nbytes);
		if (ult_lec == -1) {
			perror("Error en el recv");
			return leidos; /* error */
		}

		leidos += ult_lec;
		nbytes -= ult_lec;

		if (ult_lec == 0 && nbytes) {
			return leidos;
		}

	}

	/* Devuelve la cantidad de bytes recibidor. */
	return leidos;
}

int Socket::getFd() {
	return sockfd;
}

void Socket::closeSock() {
	close(this->sockfd);
}
